/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.jeff.springWebTestNG.concurrent;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class emulates using CompletionService to implement a web-crawler
 * @author winnie
 */
public class CompletionServiceEmulator {
    private static final int N_IMG = 3;
    private static final long startTime = System.currentTimeMillis();
    private ExecutorService exec = Executors.newFixedThreadPool(3);
    private CompletionService completionService = new ExecutorCompletionService(exec);
    
    
    
    public void start() {
        WebInfo webInfo = new WebInfo();
        
        for (int i = 1; i <= N_IMG; i++) {
            completionService.submit(new ImageRenderTask(i, (N_IMG - i)*10));
        }
        
        HtmlInfo htmlInfo = renderHtml();
        List<ImageInfo> images = new ArrayList<ImageInfo>();
        
        webInfo.html = htmlInfo;
        webInfo.images = images;
        
        try {
            for (int i = 1; i <= N_IMG; i++) {
                Future<ImageInfo> imageFuture = completionService.take();
                ImageInfo image = null;
                try {
                    //timeout will never hit since anything 'take()' from a CompletionService is already ready to use
                    image = imageFuture.get(1, TimeUnit.MILLISECONDS);
                } catch (ExecutionException ex) {
                    Logger.getLogger(CompletionServiceEmulator.class.getName()).log(Level.SEVERE, null, ex);
                } catch (TimeoutException ex) {
                    //in case of timeout, usually we simply give a default image to hold the place
                    image = getDefaultImage();
                }
                
                images.add(image);
            }
            
            //until now, the webInfo content is ready to stream to user;
            System.out.println("All task Completed @" + (System.currentTimeMillis() - startTime));
        } catch (InterruptedException ex) {
            Logger.getLogger(CompletionServiceEmulator.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private ImageInfo getDefaultImage() {
        ImageInfo ret = new ImageInfo(0);
        System.out.println("Default Image Created @" + (System.currentTimeMillis() - startTime));
        return ret;
    }
    
    private class ImageRenderTask implements Callable<ImageInfo> {
        int imageId;
        long renderTime;

        public ImageRenderTask(int imageId, long renderTime) {
            this.imageId = imageId;
            this.renderTime = renderTime;
        }
         
        @Override
        public ImageInfo call() throws Exception {
            return renderImage(imageId, renderTime);
        }
        
    }
    
    //method of rendering html, which is very fast since only pure text processing
    private HtmlInfo renderHtml() {
        try {
            Thread.sleep(10);
            System.out.println("HTML Created @" + (System.currentTimeMillis() - startTime));
        } catch (InterruptedException ex) {
            Logger.getLogger(CompletionServiceEmulator.class.getName()).log(Level.SEVERE, null, ex);
        }
        return new HtmlInfo();
    }
    
    private ImageInfo renderImage(int id, long renderTime) {
        try {
            Thread.sleep(renderTime);
            System.out.println("Image[" + id + "] Created @" + (System.currentTimeMillis() - startTime));
        } catch (InterruptedException ex) {
            Logger.getLogger(CompletionServiceEmulator.class.getName()).log(Level.SEVERE, null, ex);
        }
        return new ImageInfo(id);
    }
    
    private class WebInfo {
        HtmlInfo html;
        List<ImageInfo> images;

        public WebInfo() {
        }

        public WebInfo(HtmlInfo html, List<ImageInfo> images) {
            this.html = html;
            this.images = images;
        }
    }
    
    private class HtmlInfo {
    }
    
    private class ImageInfo {
        int id;

        public ImageInfo(int id) {
            this.id = id;
        }
        
    }
}
